有了泛型之後,開發人員不用自己強制轉換型別,compiler會根據宣告的時候,型別參數的型別幫你轉,程式碼看起來比較簡潔,型別轉換也比較安全,因為compiler會幫你檢查型別對不對。
import java.util.ArrayList;
import java.util.List;
public class TestGenericsList {
public static void main(String[] args) {
// 宣告一個List,指定泛型類型
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
// 從List中取出元素時,不需要進行轉換
for (String s : list) {
System.out.println(s);
}
}
}
但是一開始就指定型別參數(type parameters)也有個缺點,相較於原始類型(raw types),會讓程式碼變得比較沒那麼彈性,舉例來說,今天有個水果商店賣兩種水果,一種是Apple
,一種是Orange
,他們都實作了Fruit
,因為價格都不一樣,最後要算錢的時候,再使用sumFruitPrice
加總所有水果的金額。
但因為sumFruitPrice
一開始就指定了List
的參數的型別,compiler檢查型別的時候發現不一樣,會跳出incompatible types這個錯誤訊息,無法彈性地接受不一樣的水果,既使Apple
和Orange
都是實作Fruit
,compiler還是會認定不合法。
import java.util.ArrayList;
import java.util.List;
public class FruitStore {
public static void main(String[] args) {
List<Apple> appleList = new ArrayList<>();
Apple apple1 = new Apple(10);
Apple apple2 = new Apple(20);
appleList.add(apple1);
appleList.add(apple2);
List<Orange> orangeList = new ArrayList<>();
Orange orange = new Orange(15);
orangeList.add(orange);
int sum = sumFruitPrice(appleList, 0);
sum = sumFruitPrice(orangeList, sum);
System.out.println("Sum of fruit prices: " + sum);
}
public static int sumFruitPrice(List<Fruit> fruits, int sum) {
for (int i = 0; i < fruits.size(); i++) {
sum += fruit.getPrice();
}
return sum;
}
}
interface Fruit {
public int getPrice();
}
class Apple implements Fruit {
private int price;
public Apple(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
class Orange implements Fruit {
private int price;
public Orange(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
為了讓泛型更彈性,難道我們只能選擇不安全的原始類型(raw types)嗎?這邊提供了另一種方式,可以安全又彈性的使用泛型,把泛型的型別參數(type parameters)設為Bounded wildcard types(ex: List<? extends Fruit>
),就可以在還不知道參數型別的情況下,讓泛型類型(generic types)接受不一樣型別的參數,並且限制範圍。
上面的範例把sumFruitPrice
的參數fruits
做了調整之後,sumFruitPrice
就可以吃進各種水果,雖然中間還是要自己做強制轉型,但是至少後面要加新的水果,sumFruitPrice
不需要再做調整就可以加總水果的金額。
import java.util.ArrayList;
import java.util.List;
public class TestGenericsList {
public static void main(String[] args) {
// 宣告一個List,指定泛型類型
List<Apple> appleList = new ArrayList<>();
Apple apple1 = new Apple(10);
Apple apple2 = new Apple(20);
appleList.add(apple1);
appleList.add(apple2);
List<Orange> orangeList = new ArrayList<>();
Orange orange = new Orange(15);
orangeList.add(orange);
int sum = sumFruitPrice(appleList, 0);
sum = sumFruitPrice(orangeList, sum);
System.out.println("Sum of fruit prices: " + sum);
}
public static int sumFruitPrice(List<? extends Fruit> fruits, int sum) {
for (int i = 0; i < fruits.size(); i++) {
Fruit fruit = (Fruit) fruits.get(i);
sum += fruit.getPrice();
}
return sum;
}
}
interface Fruit {
public int getPrice();
}
class Apple implements Fruit {
private int price;
public Apple(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
class Orange implements Fruit {
private int price;
public Orange(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
Bounded wildcard types這個方法比起原始類型(raw types)更安全的地方在於,可以讓compiler先檢查是否有不合法的型別參數(type parameters),如果有就先跳出錯誤訊息(incompatible types),不會等到runtime才發現。
List<Integer> bananaList = new ArrayList<>();
Integer banana = Integer.valueOf(14);
bananaList.add(banana);
sum = sumFruitPrice(bananaList, sum);
參考資料: